<?php

namespace App\Models\XS;

use Illuminate\Support\Facades\DB;
use XSException, XSDocument, Carbon\Carbon;
use Illuminate\Pagination\Paginator;

class ResumeXS extends CmsXS
{
    /** 读取配置初始化
     * ResumeXS constructor.
     */
    public function __construct()
    {
        parent::__construct(config_path('xunsearch_resume.ini'));
    }

    /**
     * 迅搜简历统计数
     * @return bool|int
     */
    public static function getResumeXSTotal()
    {
        try {
            return (new ResumeXS())->search->dbTotal;
        } catch (XSException $e) {
            return false;
        }
    }

    /** 查找简历及所需要信息
     * @param $ids
     * @return mixed
     */
    private function findResume($ids)
    {
        //审核通过的,公开的
        $resumes = Resume::where('resume_status', 1)
            ->whereIn('id', $ids)
            ->with('resume_work_exp', 'resume_pro_exp',
                'resume_skill', 'resume_edu_exp')
            ->get(['id', 'intentionjobs', 'introduction', 'jobarea1',
                'jobarea2', 'jobarea3', 'jobarea4', 'jobarea5', 'work_year',
                'birthday', 'jobsort1', 'jobsort2', 'jobsort3', 'jobsort4',
                'jobsort5', 'sex', 'education', 'homeaddress', 'census',
                'chkphoto_open', 'avatar', 'updatetime', 'name']);

        return $resumes;
    }

    /** 添加一条或多条简历记录
     * @param $id
     * @return bool
     */
    public static function storeResume($id)
    {
        try {
            if (is_null($id)) return false;
            $xs = new ResumeXS();
            $resumes = $xs->findResume(is_array($id) ? $id : func_get_args());
            if (count($resumes) < 1) {
                return false;
            }
            $data = $resumes->toArray();

            $index = $xs->index;

            // 开启缓冲区，默认 4MB，如 $index->openBuffer(8) 则表示 8MB
            $bl = count($data) > self::$OPEN_BUFFER_COUNT;
            if ($bl) $index->openBuffer();
            foreach ($data as $item) {
                $doc = new XSDocument;
                $doc->setFields($item); // 用数组进行批量赋值
                $index->add($doc); // 添加
            }
            // 关闭缓冲区，必须和 openBuffer 成对使用
            if ($bl) $index->closeBuffer();

            return true;
        } catch (XSException $e) {
            return false;
//            echo $e;               // 直接输出异常描述
//            if (defined('DEBUG'))  // 如果是 DEBUG 模式，则输出堆栈情况
//                echo "\n" . $e->getTraceAsString() . "\n";
        }
    }

    /** 更新一条或多条简历记录
     * @param $id
     * @return bool
     */
    public static function updateResume($id)
    {
        try {
            //查找简历
            if (is_null($id)) return false;
            $xs = new ResumeXS();
            $resumes = $xs->findResume(is_array($id) ? $id : func_get_args());
            if (count($resumes) < 1) {
                return false;
            }
            $data = $resumes->toArray();

            $index = $xs->index;

            // 开启缓冲区，默认 4MB，如 $index->openBuffer(8) 则表示 8MB
            $bl = count($data) > self::$OPEN_BUFFER_COUNT;
            if ($bl) $index->openBuffer();
            foreach ($data as $item) {
                $doc = new XSDocument;
                $doc->setFields($item); // 用数组进行批量赋值
                $index->update($doc); // 添加
            }
            // 关闭缓冲区，必须和 openBuffer 成对使用
            if ($bl) $index->closeBuffer();

            return true;
        } catch (XSException $e) {
            return false;
        }
    }

    /** 删除一条或多条简历记录
     * @param $id
     * @return bool
     */
    public static function deleteResume($id)
    {
        try {
            if (empty($id)) return false;

            $xs = new ResumeXS();

            //把id转为数组
            $ids = is_array($id) ? $id : func_get_args();

            $index = $xs->index;

            $xs->logXSDaily('后台手动删除简历---------', false);
            foreach ($ids as $v) {
                try {
                    $index->del($v);
                    $xs->logXSDaily("id=$v 简历删除成功", false);
                } catch (XSException $e) {
                    $xs->logXSDaily("id=$v 简历删除失败 - " . $e->getMessage(), false);
                }
            }

            //强制刷新迅搜
            $index->flushIndex();

            return true;
        } catch (XSException $e) {
            return false;
        }
    }

    /** 清空所有简历
     * @return bool
     */
    public static function clean()
    {
        try {
            $xs = new ResumeXS();
            $xs->index->clean();
            return true;
        } catch (XSException $e) {
            return false;
        }
    }

    public $cms_total = 0;
    public $cms_total_ok = 0;
    public $cms_total_failed = 0;

    /** 重新插入所有简历(不用先清空,直接可以重新插入)
     * @return bool
     */
    public function storeAllResume($request)
    {

        try {
            $this->cms_total = isset($request->cms_total) ? $request->cms_total : 0;
            $this->cms_total_ok = isset($request->cms_total_ok) ? $request->cms_total_ok : 0;
            $this->cms_total_failed = isset($request->cms_total_failed) ? $request->cms_total_failed : 0;
            $index = $this->index;
            $page = Paginator::resolveCurrentPage('page');
            $paginate = 1000;//每页条数
            // 宣布开始重建索引
            if ($page == 1) {
                $index->beginRebuild();
                $this->logXS("----------------------------------------------\n", false);
                $this->logXS("开始批量导入简历 \n", false);
            }

            //每次数据库读取取1000条,减少内存消耗,//审核通过的,公开的
            $resumes = Resume::where('resume_status', 1)
                ->with('resume_work_exp', 'resume_pro_exp', 'resume_skill', 'resume_edu_exp')
                ->select(['id', 'intentionjobs', 'introduction', 'jobarea1',
                    'jobarea2', 'jobarea3', 'jobarea4', 'jobarea5', 'work_year',
                    'birthday', 'jobsort1', 'jobsort2', 'jobsort3', 'jobsort4',
                    'jobsort5', 'sex', 'education', 'homeaddress', 'census',
                    'chkphoto_open', 'avatar', 'updatetime', 'name'])
                ->orderBy('id')
                ->simplePaginate($paginate);
            $bl = count($resumes) > self::$OPEN_BUFFER_COUNT;
            if ($bl) $index->openBuffer();
            $ids_arr = [];//需要把xs_status改为1的所有id
            foreach ($resumes as $resume) {
                $ids_arr[] = $resume->id;
                $this->cms_total++;
                try {
                    $doc = new XSDocument;
                    $doc->setFields($resume->toArray()); // 用数组进行批量赋值
                    $index->update($doc); // 添加
                    $this->cms_total_ok++;
                } catch (XSException $e) {
                    $this->logXS("警告：添加第 $this->cms_total 条 id=$resume->id 简历失败 - " . $e->getMessage() . "\n", false);
                    $this->logXS($e->getTraceAsString(), false);
                    $this->cms_total_failed++;
                }
            }
            if ($bl) $index->closeBuffer();
            $ids_str = implode(',', $ids_arr);
            if ($page % 10 == 0) {
                $this->logXS("报告：累计已处理简历 $this->cms_total 条 ...成功 $this->cms_total_ok 条，失败 $this->cms_total_failed 条\n", false);
            }
            Resume::whereIn('id', $ids_arr)->update(['xs_status' => 1]);
            $result = [];
            $result['cms_total'] = $this->cms_total;
            $result['cms_total_ok'] = $this->cms_total_ok;
            $result['cms_total_failed'] = $this->cms_total_failed;
            $resumes_count = count($resumes);
            if ($resumes_count < $paginate) {
                if ($resumes_count > 0)
                    $this->logXS("报告：累计已处理简历 $this->cms_total 条 ...成功 $this->cms_total_ok 条，失败 $this->cms_total_failed 条\n", false);
                $this->logXS("完成简历导入!!!<br><br>\n", false);
                // 告诉服务器重建完毕
                $index->endRebuild();
                $result['result'] = '2';//说明是最后一页
            } else {
                $result['result'] = '1';
            }
            return $result;
        } catch (XSException $e) {
            //您还可以在重建前先调用 XSIndex::stopRebuild 直接清除这个错误状态
            $this->index->stopRebuild();
            $this->logXS("导入出错\n$e<br><br>", false);
            return false;
        }
    }

    /** 搜索,注意:返回的结构是[data=>[...],total=>123],data数组里的才是数据
     *
     * $input的可以传入的键如下
     *
     *   键名             类型                     备注
     * jobsort        int数组或英文逗号分隔字符串  通过cms_coll_position表转为中文后进行全文检索,or链接
     * jobarea        int数组或英文逗号分隔字符串  传入的id会增加一条父id不限的条件,例如查找[椒江],则会查出[椒江],[台州不限].or链接
     * sex            int                      性别
     * homeaddress    int                      现居住地
     * census         int                      户籍
     * chkphoto_open  int                      是否有照片并且是公开状态
     * keyword        string                   关键字,可以用逗号隔开关键字,or链接
     * name           string                   简历真实姓名
     * intentionjobs  string                   求职意向
     * shield         int数组或英文逗号分隔字符串  数组里的id简历不会被查出来
     * census_other   int                      其他户籍,该id的户籍不会被查出来
     * age            int                      年龄范围 传入的id是cms_coll_options表中的age_stint
     * updatetime     int                      最近更新 传入天数
     * haveworkexp    int                      有无经验
     * work_year      int                      工作经验及以上
     * education      int                      学历及以上
     *
     * @param $input
     * @param int $curPage 查询第几页,从1开始
     * @param int $perPage 一页个数
     * @param bool|true $getTotal 是否获取总数,默认获取
     * @param bool|true $orderByTime 是否按照时间排序
     * @return array
     */
    public function searchResume($input, $curPage = 1, $perPage = 20, $getTotal = true, $orderByTime = true)
    {
        try {
            $query = $this->makeQueryWithInput($input);
            $search = $this->search->setQuery($query);

            //年龄
            if (!empty($input['age'])) {
                $age = $input['age'];//参数
                $from = null;
                $to = null;

                //当前年份
                $year = Carbon::now()->year;
                //最低年龄
                if (!empty($age['age_above'])) {
                    $to = strtotime(($year - $age['age_above']) . '-01-01');
                }

                //最高年龄
                if (!empty($age['age_below'])) {
                    $from = strtotime(($year - $age['age_below']) . '-01-01');
                }

                $search->addRange('birthday', $from, $to);
            }

            //更新日期
            if (!empty($input['updatetime'])) {
                $from = Carbon::now()->subDays($input['updatetime'])->getTimestamp();
                $search->addRange('updatetime', $from, null);
            }

            //有无工作经验
            if (!empty($input['haveworkexp'])) {
                $search->addRange('work_year', 11, null);
            }

            //工作经验
            if (!empty($input['work_year'])) {
                $search->addRange('work_year', $input['work_year'], null);
            }

            //学历
            if (!empty($input['education'])) {
                $search->addRange('education', $input['education'], null);
            }

            //按照时间排列
            if ($orderByTime) {
                $sorts = [];
                if (array_key_exists('by_day', $input) && boolval($input['by_day'])) {
                    $sorts['updatetime_by_day'] = false;
                } else {
                    $sorts['updatetime'] = false;
                }
                $sorts['id_sort'] = false;
                $search->setMultiSort($sorts);
            }

            // 设置搜索语句, 分页, 偏移量
            $this->cms_curPage = $curPage;
            $this->cms_perPage = $perPage;
            $search->setLimit($getTotal ? $perPage : $perPage + 1, $perPage * ($curPage - 1));

            //执行搜索，将搜索结果文档保存在 $results 数组中
            $results = $search->search();
            $total = 0;
            if ($getTotal) {//获取搜索结果的匹配总数估算值
                $total = $search->count();
            }

            $data = [];
            foreach ($results as $result) {
                $item = (object)$result->getFields();//将xs对象转为php普通对象(stdClass)

                $item->jobarea = explode(' ', $item->jobarea);//转为数组
                $item->jobsort = explode(' ', $item->jobsort);


                $areaCount = count($item->jobarea);
                $sortCount = count($item->jobsort);

                for ($i = 1; $i <= 5; $i++) {
                    $key = $i * 3 - 1;
                    if ($areaCount > $key) {
                        $item->{'jobarea' . $i} = $item->jobarea[$key];
                    } else break;
                }

                for ($i = 1; $i <= 5; $i++) {
                    $key = $i - 1;
                    if ($sortCount > $key) {
                        $item->{'jobsort' . $i} = $item->jobsort[$key];
                    } else break;
                }

                unset($item->jobarea);
                unset($item->jobsort);

                $item->birthday = date('Y-m-d H:i:s', $item->birthday);
                $item->updatetime = date('Y-m-d H:i:s', $item->updatetime);

                $item->homeaddress = last(explode(' ', $item->homeaddress));
                $item->census = last(explode(' ', $item->census));

                unset($item->other_important);
                unset($item->other);
                $data[] = $item;
            }

            return ['data' => $data, 'total' => $total];
        } catch (XSException $e) {
            return ['data' => [], 'total' => 0];
        }
    }


    public function makeQueryWithInput($input)
    {
        $query = '';

        if (empty($input)) return $query;

        //工作岗位cid转为中文名,全文检索
        if (!empty($input['jobsort'])) {
            $query .= self::jobSortToQuery($input['jobsort']);
        }

        //工作地区
        if (!empty($input['jobarea'])) {
            $query .= self::jobAreaToQuery($input['jobarea'], 'jobarea');
        }

        //指定搜索的key
        $selfKey = ['sex', 'homeaddress', 'census', 'chkphoto_open'];

        foreach ($selfKey as $key) {
            if (empty($input[$key])) continue;
            $query .= "$key:$input[$key] ";
        }

        //关键字(全文搜索,如果多个关键字OR连接)
        if (!empty($input['keyword'])) {
            $values = $input['keyword'];
            //不是数组,转为数组
            $values = is_array($values) ? $values : explode(',', $values);
            $query .= implode(' OR ', $values) . ' ';
        }

        //名字搜索
        if (!empty($input['name'])) {
            $key = !empty($input['keyword']) ? 'OR name:' : 'name:';
            $query .= $key . $input['name'] . ' ';
        }

        //意向搜索
        if (!empty($input['intentionjobs'])) {
            $key = !empty($input['keyword']) ? 'OR intentionjobs:' : 'intentionjobs:';
            $query .= $key . $input['intentionjobs'] . ' ';
        }

        //黑名单id
        if (!empty($input['shield'])) {
            $values = $input['shield'];
            $values = is_array($values) ? $values : explode(',', $values);
            foreach ($values as $item) {
                $query .= "-id:$item ";
            }
        }

        //其他户籍
        if (!empty($input['census_other'])) {
            $query .= "-census:" . $input['census_other'] . " ";
        }

//        $query='jobarea:331004 jobarea:331003 education:44';
        return rtrim($query);
    }

    /** 每分钟的定时执行代码
     * @return bool
     */
    public static function timerUpdate()
    {
        $xs = new ResumeXS();
        try {
            $xs->logXSDaily('正在执行定时更新简历...', false);
            $index = $xs->index;

            $addCount = 0;//插入个数
            $deleteCount = 0;//删除个数

            //更新 //需要被同步到xunsearch
            Resume::where(function($q){
                $q->where(['resume_status' => 1, 'xs_status' => 0, 'bkresume_status'=>1]);
            })
                ->with('resume_work_exp', 'resume_pro_exp', 'resume_skill', 'resume_edu_exp')
                ->select(['id', 'intentionjobs', 'introduction', 'jobarea1',
                    'jobarea2', 'jobarea3', 'jobarea4', 'jobarea5', 'work_year',
                    'birthday', 'jobsort1', 'jobsort2', 'jobsort3', 'jobsort4',
                    'jobsort5', 'sex', 'education', 'homeaddress', 'census',
                    'chkphoto_open', 'avatar', 'updatetime', 'name'])
                ->chunk(1000, function ($resumes) use ($index, $xs, &$addCount) {
                    $bl = count($resumes) > self::$OPEN_BUFFER_COUNT;
                    if ($bl) $index->openBuffer();
                    foreach ($resumes as $resume) {
                        try {
                            $doc = new XSDocument;
                            $doc->setFields($resume->toArray()); // 用数组进行批量赋值
                            $index->update($doc); // 添加
                            ++$addCount;
                            \DB::table('resume')->where('id', $resume->id)->update(['xs_status' => 1]);
                        } catch (XSException $e) {
                            $xs->logXSDaily("id=$resume->id 简历添加失败 - " . $e->getMessage(), false);
                        }
                    }
                    if ($bl) $index->closeBuffer();
                });

            $xs->logXSDaily("成功更新了 $addCount 条简历", false);

            //删除
            $ids = \DB::table('resume')
                ->select(['id', 'xs_status'])
                ->where(function($q){
                    $q->where('resume_status', '!=', 1)->orWhere('bkresume_status', '!=', 1);
                })
                ->where('xs_status', 0)//需要被同步到xunsearch
                ->pluck('id');

            foreach ($ids as $id) {
                try {
                    $index->del($id);
                    \DB::table('resume')->where('id', $id)->update(['xs_status' => 1]);
                    $xs->logXSDaily("id=$id 简历删除成功", false);
                    $deleteCount += 1;
                } catch (XSException $e) {
                    $xs->logXSDaily("id=$id 简历删除失败 - " . $e->getMessage(), false);
                }
            }

            $xs->logXSDaily("成功删除了 $deleteCount 条简历", false);

            //强制刷新迅搜
            $index->flushIndex();
            return true;
        } catch (XSException $e) {
            $xs->logXSDaily("执行出错:" . $e, false);
            return false;
        }
    }
}
